package com.skyline.energy.definition;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.ArrayUtils;

import com.skyline.energy.annotation.BatchParam;
import com.skyline.energy.annotation.GenericTable;
import com.skyline.energy.annotation.Param;
import com.skyline.energy.exception.DaoGenerateException;
import com.skyline.energy.parser.ExpressionParser;
import com.skyline.energy.parser.ParsedExpression;
import com.skyline.energy.utils.CommonUtils;
import com.skyline.energy.utils.SqlFormatter;

public class BaseJdbcDefinition extends ShardDefinition {
	private ParsedExpression parsedSql;
	private String preparedSql;
	private Method[] getterMethods;
	private Integer[] parameterIndexes;
	private Integer[] genericIndexes;
	private Integer[] batchParamIndexes;
	
	public String getPreparedSql() {
		return preparedSql;
	}

	public Method[] getGetterMethods() {
		return getterMethods;
	}

	public Integer[] getParameterIndexes() {
		return parameterIndexes;
	}

	public Integer[] getGenericIndexes() {
		return genericIndexes;
	}

	public Integer[] getBatchParamIndexes() {
		return batchParamIndexes;
	}
	
	public ParsedExpression getParsedSql() {
		return parsedSql;
	}

	public void setParsedSql(ParsedExpression parsedSql) {
		this.parsedSql = parsedSql;
	}
	
	protected void parseParameterAnnotations(Annotation[][] annotations, Map<String, Integer> paramIndexes,
			Map<String, Integer> batchParamIndexMap, Class<?>[] paramTypes) throws DaoGenerateException {
		for (int index = 0; index < annotations.length; index++) {

			for (Annotation annotation : annotations[index]) {
				Class<? extends Annotation> annotationType = annotation.annotationType();
				if (Param.class.equals(annotationType)) {
					Param param = (Param) annotation;
					String value = param.value();
					paramIndexes.put(value, index);
				}
				if (BatchParam.class.equals(annotationType) && batchParamIndexMap != null) {
					BatchParam param = (BatchParam) annotation;
					String value = param.value();
					batchParamIndexMap.put(value, index);
					batchParamIndexes = (Integer[]) ArrayUtils.add(batchParamIndexes, new Integer(index));

					if (paramTypes[index] == null || !paramTypes[index].isArray()) {
						throw new DaoGenerateException("@BatchParam can only on an array");
					}

				}
				if (GenericTable.class.equals(annotationType)) {
					GenericTable genericTable = (GenericTable) annotation;
					int order = genericTable.index();
					genericIndexes = CommonUtils.addElemToArray(genericIndexes, index, order);
				}
			}
		}
	}

	protected void parseSql(String oriSql, Class<?>[] paramTypes, Map<String, Integer> paramIndexes)
			throws DaoGenerateException {
		parseSql(oriSql, paramTypes, paramIndexes, null);
	}

	protected void parseSql(String oriSql, Class<?>[] paramTypes, Map<String, Integer> paramIndexes,
			Map<String, Integer> batchParamIndexes) throws DaoGenerateException {
		parsedSql = ExpressionParser.parseExpression(oriSql);
		List<String> parameterNames = parsedSql.getParameterNames();
		Object[] gettersAndIndexes = null;
		if (batchParamIndexes == null) {
			gettersAndIndexes = CommonUtils.getGettersAndIndexes(parameterNames, paramIndexes, paramTypes);
		} else {
			gettersAndIndexes = CommonUtils.getGettersAndIndexes(parameterNames, paramIndexes, batchParamIndexes,
					paramTypes);
		}
		Method[] getterMethods = (Method[]) gettersAndIndexes[0];
		Integer[] parameterIndexes = (Integer[]) gettersAndIndexes[1];

		String preparedSql = CommonUtils.getSql(parsedSql);

		this.getterMethods = getterMethods;
		this.parameterIndexes = parameterIndexes;
		this.preparedSql = preparedSql;
	}
	
	
	
	public String getActualSql(Object[] args) {
		String[] tableNames = null;
		String sql;
		
		if(genericIndexes != null) {
			for(Integer index : genericIndexes) {
				if (index != null) {
					Object arg = args[index];
					if (arg != null) {
						tableNames = (String[]) ArrayUtils.add(tableNames, arg.toString());
					}
				}
			}
		}
		
		if(tableNames != null) {
			sql = SqlFormatter.formatSql(preparedSql, tableNames);
		} else {
			sql = preparedSql;
		}
		
		return sql;
	}
	
}
